/*
 * =====================================================================================
 *
 *       Filename:  01_open.c
 *
 *    Description:
 *
 *        Version:  1.0
 *        Created:  2019年06月10日 23时13分39秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/select.h>

typedef unsigned char uint8_t;

struct serial_config {
    speed_t baud_rate;

    /* data_bit: 5,6,7,8 */
    uint8_t data_bit;

    /* 
     * parity_bit: 1. None: 'n', 'N'  没有校验位
     *             2. Even: 'e', 'E'  bit为1的个数是偶数个
     *             3. Odd : 'o', 'O'  bit为1的个数是奇数个
     *             Not In  POSIX
     *             4. Space:'s', 'S'  校验位总为0
     *             5. Mark: 'm', 'M'  校验位总为1
     */
    char    parity_bit;
    uint8_t stop_bit;
    char    flow_ctrl;
};

int serial_open(const char *pathname)
{
    int fd = open(pathname, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if(fd < 0) {
        fprintf(stderr, "%s: open(%s), %s\n", __func__, pathname, strerror(errno));
        return -1;
    }
    return fd;
}

int serial_config_baud_rate(struct termios *term, speed_t baud_rate)
{
    if(cfsetispeed(term, baud_rate) < 0) {
        fprintf(stderr, "%s: tcgetattr, %s\n", __func__, strerror(errno));
        return -1;
    }

    if(cfsetospeed(term, baud_rate) < 0) {
        fprintf(stderr, "%s: tcgetattr, %s\n", __func__, strerror(errno));
        return -1;
    }
    return 0;
}

int serial_config_data_bit(struct termios *term, uint8_t data_bit)
{
    /* 先取反再与操作:把对应位都置0 */
    switch(data_bit) {
    case 5:
        term->c_cflag &= ~(CSIZE);
        term->c_cflag |= CS5;
        break;
    case 6:
        term->c_cflag &= ~(CSIZE);
        term->c_cflag |= CS6;
        break;
    case 7:
        term->c_cflag &= ~(CSIZE);
        term->c_cflag |= CS7;
        break;
    case 8:
        term->c_cflag &= ~(CSIZE);
        term->c_cflag |= CS8;
        break;
    default:
        fprintf(stderr, "%s: data_bit out of range\n", __func__);
        return -1;
    }
    return 0;
}

int serial_config_parity_bit(struct termios *term, char parity_bit)
{
    switch(parity_bit) {
    case 'n':
    case 'N':
        term->c_cflag &= ~PARENB; /* 关闭校验使能位 */
        term->c_iflag &= ~INPCK;  /* 关闭输入校验检查 */ 
        break;
    case 'e':
    case 'E':
        term->c_cflag |= PARENB;  /* 开启校验使能 */
        term->c_iflag |= INPCK;   /* 开启输入校验检查 */
        term->c_cflag &= ~PARODD; /* 设置偶校验 */
        term->c_iflag |= ISTRIP;  /* 删除数据位的第八位 */
        break;
    case 'o':
    case 'O':
        term->c_cflag |= PARENB;  /* 开启校验使能 */
        term->c_iflag |= INPCK;   /* 开启输入校验检查 */
        term->c_cflag &= ~PARODD; /* 设置奇校验 */
        term->c_iflag |= ISTRIP;  /* 删除数据位的第八位 */
        break;
#if 0
    case 's':
    case 'S':
        fprintf(stderr, "%s: We Do No Support Space\n", __func__);
        break;
    case 'm':
    case 'M':
        fprintf(stderr, "%s: We Do Not Support Mark\n", __func__);
        break;
#endif
    default:
        fprintf(stderr, "%s: parity_bit out of range\n", __func__);
        return -1;
    }
    return 0;
}

int serial_config_stop_bit(struct termios *term, uint8_t stop_bit)
{
    switch(stop_bit) {
    case 1:
        term->c_cflag &= ~CSTOPB; /* 1位停止位 */
        break;
    case 2:
        term->c_cflag |= CSTOPB;  /* 2位停止位 */
        break;
    default:
        fprintf(stderr, "%s: stop_bit out of range\n", __func__);
        return -1;
    }
    return 0;
}

int serial_config_flow_ctrl(struct termios *term, char flow_ctrl)
{
    switch(flow_ctrl) {
    case 'n':
    case 'N':
        term->c_cflag &= ~CRTSCTS;
        break;
    case 'h':
    case 'H':
        term->c_cflag |= CRTSCTS;
        break;
    case 's':
    case 'S':
        term->c_iflag |= (IXON | IXOFF | IXANY);
        break;
    default:
        fprintf(stderr, "%s: We Do Not Support %c\n", __func__, flow_ctrl);
        return -1;
    }
    return 0;
}

int serial_config_other(struct termios *term)
{
    term->c_cflag |= CREAD;
    term->c_cflag |= CLOCAL;
    term->c_oflag &= ~OPOST;
    term->c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    return 0;
}

int serial_config(int fd, struct serial_config *config)
{
    struct termios term;
    if(tcgetattr(fd, &term) < 0) {
        fprintf(stderr, "%s: tcgetattr, %s\n", __func__, strerror(errno));
        return -1;
    }
    /* 1.波特率 */
    if(serial_config_baud_rate(&term, config->baud_rate) < 0) {
        return -1;
    }
    /* 2.数据位 */
    serial_config_data_bit(&term, config->data_bit);
    /* 3.校验位 */
    serial_config_parity_bit(&term, config->parity_bit);
    /* 4.停止位 */
    serial_config_stop_bit(&term, config->stop_bit);
    /* 5.流控 */
    serial_config_flow_ctrl(&term, config->flow_ctrl);
    /* 6.其他重要设置 */
    serial_config_other(&term);

    if(tcsetattr(fd, TCSANOW, &term) < 0) {
        fprintf(stderr, "%s: tcsetattr, %s\n", __func__, strerror(errno));
        return -1;
    }

    return 0;
}

static const char *pathname = "/dev/ttyS0";

int main(int argc, char **argv)
{
    int fd = serial_open(pathname);
    if(fd < 0) {
        return 0;
    }

    struct serial_config config = {
        .baud_rate = B115200,
        .data_bit = 7,
        .parity_bit = 'O',
        .stop_bit = 1,
        .flow_ctrl = 'N',
    };
    printf("Open %s Success\n", pathname);
    if(serial_config(fd, &config) < 0) {
        return -1;
    }
    printf("Set Serial OK\n");

    fd_set rset;
    fd_set wset;
    int fd_max = -1;
    while(1) {
        FD_ZERO(&rset);
        FD_ZERO(&wset);
        FD_SET(fd, &rset);
        FD_SET(fd, &wset);
        fd_max = fd_max<fd?fd:fd_max;

        int ret = select(fd_max + 1, &rset, &wset, NULL, NULL);
        if(ret < 0) {
            fprintf(stderr, "%s: select(), %s\n", __func__, strerror(errno));
            continue;
        }
        if(!FD_ISSET(fd, &rset)) {
            continue;
        }
        char buff[1024] = {0};
        
        ret = read(fd, buff, sizeof(buff) - 1);
        if(ret < 0) {
            fprintf(stderr, "%s: read(), %s\n", __func__, strerror(errno));
            continue;
        }

        fprintf(stdout, "Read: [%.*s]\n", ret, buff);

        if(FD_ISSET(fd, &wset)) {
            ret = write(fd, buff, ret);
            if(ret < 0) {
                fprintf(stderr, "%s: write(), %s\n", __func__, strerror(errno));
            }
        }
        else {
            fprintf(stdout, "remote serial is busy\n");
            //此时接收到的数据直接丢弃，这个会不会造成问题呢?
        }
    }

    close(fd);
    return 0;
}



